﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Notoric.Model.Validation
{
    /// <summary>
    /// An interface that a class should implement to be able to validate
    /// new values for its properties.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The purpose of this interface is to offer to the user of its instance to check
    /// a new value for some of its properties before actually setting it and potentially
    /// causing an argument exception to be thrown.
    /// </para>
    /// <para>
    /// This interface cannot prevent the user from setting an invalid property value,
    /// so the implementor should still check the value in the property setter
    /// (for example using the validation rules) and throw an argument exception if this happens.
    /// </para>
    /// </remarks>
    /// <seealso cref="Rule"/>
    public interface IValidator
    {
        /// <summary>
        /// Returns an array of the validation rules for a given property.
        /// </summary>
        /// <param name="propertyName">
        /// <para>
        /// Type: <see cref="System.String"/>
        /// </para>
        /// <para>
        /// Name of the property for which the validation rules are searched
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="System.Array"/> of <see cref="Rule"/>
        /// </para>
        /// The collection of rules that constraint the value of the property
        /// or <see langword="null"/> if no rule is imposed.
        /// </returns>
        Rule[] GetValidationRules(string propertyName);

        /// <summary>
        /// Checks if a given value is valid for a given property.
        /// </summary>
        /// <param name="propertyName">
        /// <para>
        /// Type: <see cref="System.String"/>
        /// </para>
        /// <para>
        /// Name of the validated property
        /// </para>
        /// </param>
        /// <param name="value">
        /// <para>
        /// Type: <see cref="System.Object"/>
        /// </para>
        /// <para>
        /// Value to be checked
        /// </para>
        /// </param>
        /// <param name="brokenRules">
        /// <para>
        /// Type: <see cref="System.Array"/> of <see cref="Rule"/>
        /// </para>
        /// <para>
        /// If the validation fails, the array will contain the validation rules that were broken by the <paramref name="value"/>,
        /// otherwise it will be set to <see langword="null"/>.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="System.Boolean"/>
        /// </para>
        /// <para>
        /// The validation result:
        /// <see langword="true"/> if the validation is successful, <see langword="false"/> otherwise.
        /// </para>
        /// </returns>
        bool ValidatePropertyValue(string propertyName, object value, out Rule[] brokenRules);

        /// <summary>
        /// Checks if a given value is valid for a given property and if the validation succeeds
        /// sets the value to the property.
        /// </summary>
        /// <param name="propertyName">
        /// <para>
        /// Type: <see cref="System.String"/>
        /// </para>
        /// <para>
        /// Name of the validated property
        /// </para>
        /// </param>
        /// <param name="value">
        /// <para>
        /// Type: <see cref="System.Object"/>
        /// </para>
        /// <para>
        /// Value to be checked and set
        /// </para>
        /// </param>
        /// <param name="brokenRules">
        /// <para>
        /// Type: <see cref="System.Array"/> of <see cref="Rule"/>
        /// </para>
        /// <para>
        /// If the validation fails, the array will contain the validation rules that were broken by the <paramref name="value"/>,
        /// otherwise it will be set to <see langword="null"/>.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="System.Boolean"/>
        /// </para>
        /// <para>
        /// The validation result:
        /// <see langword="true"/> if the validation is successful (and the value was set to the property), <see langword="false"/> otherwise.
        /// </para>
        /// </returns>
        /// <exception cref="ValidationException">
        /// The <paramref name="value"/> has not the correct type.<br/>
        /// -or-<br/>
        /// No property named <paramref name="propertyName"/> exists on the class.<br/>
        /// -or-<br/>
        /// The property exists, but is not writeable.
        /// </exception>
        bool ValidateAndSetPropertyValue(string propertyName, object value, out Rule[] brokenRules);
    }
}
